/*
 * Copyright 1999-2011 Alibaba Group.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.swak.utils;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.net.URL;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.alibaba.fastjson.util.ParameterizedTypeImpl;

import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtMethod;
import javassist.NotFoundException;

/**
 * ReflectUtils
 *
 * @author qian.lei
 */
public final class ReflectUtils {

	public static final String PARAM_CLASS_SPLIT = ",";
	public static final String EMPTY_PARAM = "void";

	/**
	 * void(V).
	 */
	public static final char JVM_VOID = 'V';

	/**
	 * boolean(Z).
	 */
	public static final char JVM_BOOLEAN = 'Z';

	/**
	 * byte(B).
	 */
	public static final char JVM_BYTE = 'B';

	/**
	 * char(C).
	 */
	public static final char JVM_CHAR = 'C';

	/**
	 * double(D).
	 */
	public static final char JVM_DOUBLE = 'D';

	/**
	 * float(F).
	 */
	public static final char JVM_FLOAT = 'F';

	/**
	 * int(I).
	 */
	public static final char JVM_INT = 'I';

	/**
	 * long(J).
	 */
	public static final char JVM_LONG = 'J';

	/**
	 * short(S).
	 */
	public static final char JVM_SHORT = 'S';

	public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];

	public static final String JAVA_IDENT_REGEX = "(?:[_$a-zA-Z][_$a-zA-Z0-9]*)";

	public static final String JAVA_NAME_REGEX = "(?:" + JAVA_IDENT_REGEX + "(?:\\." + JAVA_IDENT_REGEX + ")*)";

	public static final String CLASS_DESC = "(?:L" + JAVA_IDENT_REGEX + "(?:/" + JAVA_IDENT_REGEX + ")*;)";

	public static final String ARRAY_DESC = "(?:\\[+(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "))";

	public static final String DESC_REGEX = "(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "|" + ARRAY_DESC + ")";

	public static final Pattern DESC_PATTERN = Pattern.compile(DESC_REGEX);

	public static final String METHOD_DESC_REGEX = "(?:(" + JAVA_IDENT_REGEX + ")?\\((" + DESC_REGEX + "*)\\)("
			+ DESC_REGEX + ")?)";

	public static final Pattern METHOD_DESC_PATTERN = Pattern.compile(METHOD_DESC_REGEX);

	public static final Pattern GETTER_METHOD_DESC_PATTERN = Pattern
			.compile("get([A-Z][_a-zA-Z0-9]*)\\(\\)(" + DESC_REGEX + ")");

	public static final Pattern SETTER_METHOD_DESC_PATTERN = Pattern
			.compile("set([A-Z][_a-zA-Z0-9]*)\\((" + DESC_REGEX + ")\\)V");

	public static final Pattern IS_HAS_CAN_METHOD_DESC_PATTERN = Pattern
			.compile("(?:is|has|can)([A-Z][_a-zA-Z0-9]*)\\(\\)Z");

	private static final ConcurrentMap<String, Class<?>> DESC_CLASS_CACHE = new ConcurrentHashMap<String, Class<?>>();

	private static final ConcurrentMap<String, Class<?>> NAME_CLASS_CACHE = new ConcurrentHashMap<>();

	private static final ConcurrentMap<String, Method> SIGNATURE_METHODS_CACHE = new ConcurrentHashMap<>();

	public static boolean isPrimitives(Class<?> cls) {
		if (cls.isArray()) {
			return isPrimitive(cls.getComponentType());
		}
		return isPrimitive(cls);
	}

	public static boolean isPrimitive(Class<?> cls) {
		return cls.isPrimitive() || cls == String.class || cls == Boolean.class || cls == Character.class
				|| Number.class.isAssignableFrom(cls) || Date.class.isAssignableFrom(cls);
	}

	public static Class<?> getBoxedClass(Class<?> c) {
		if (c == int.class) {
			c = Integer.class;
		} else if (c == boolean.class) {
			c = Boolean.class;
		} else if (c == long.class) {
			c = Long.class;
		} else if (c == float.class) {
			c = Float.class;
		} else if (c == double.class) {
			c = Double.class;
		} else if (c == char.class) {
			c = Character.class;
		} else if (c == byte.class) {
			c = Byte.class;
		} else if (c == short.class) {
			c = Short.class;
		}
		return c;
	}

	/**
	 * is compatible.
	 *
	 * @param c class.
	 * @param o instance.
	 * @return compatible or not.
	 */
	public static boolean isCompatible(Class<?> c, Object o) {
		boolean pt = c.isPrimitive();
		if (o == null) {
			return !pt;
		}

		if (pt) {
			if (c == int.class) {
				c = Integer.class;
			} else if (c == boolean.class) {
				c = Boolean.class;
			} else if (c == long.class) {
				c = Long.class;
			} else if (c == float.class) {
				c = Float.class;
			} else if (c == double.class) {
				c = Double.class;
			} else if (c == char.class) {
				c = Character.class;
			} else if (c == byte.class) {
				c = Byte.class;
			} else if (c == short.class) {
				c = Short.class;
			}
		}
		if (c == o.getClass()) {
			return true;
		}
		return c.isInstance(o);
	}

	/**
	 * is compatible.
	 *
	 * @param cs class array.
	 * @param os object array.
	 * @return compatible or not.
	 */
	public static boolean isCompatible(Class<?>[] cs, Object[] os) {
		int len = cs.length;
		if (len != os.length) {
			return false;
		}
		if (len == 0) {
			return true;
		}
		for (int i = 0; i < len; i++) {
			if (!isCompatible(cs[i], os[i])) {
				return false;
			}
		}
		return true;
	}

	public static String getCodeBase(Class<?> cls) {
		if (cls == null) {
			return null;
		}
		ProtectionDomain domain = cls.getProtectionDomain();
		if (domain == null) {
			return null;
		}
		CodeSource source = domain.getCodeSource();
		if (source == null) {
			return null;
		}
		URL location = source.getLocation();
		if (location == null) {
			return null;
		}
		return location.getFile();
	}

	/**
	 * get name. java.lang.Object[][].class => "java.lang.Object[][]"
	 *
	 * @param c class.
	 * @return name.
	 */
	public static String getName(Class<?> c) {
		if (c.isArray()) {
			StringBuilder sb = new StringBuilder();
			do {
				sb.append("[]");
				c = c.getComponentType();
			} while (c.isArray());

			return c.getName() + sb.toString();
		}
		return c.getName();
	}

	public static Class<?> getGenericClass(Class<?> cls) {
		return getGenericClass(cls, 0);
	}

	public static Class<?> getGenericClass(Class<?> cls, int i) {
		try {
			ParameterizedType parameterizedType = ((ParameterizedType) cls.getGenericInterfaces()[0]);
			Object genericClass = parameterizedType.getActualTypeArguments()[i];
			if (genericClass instanceof ParameterizedType) { // 处理多级泛型
				return (Class<?>) ((ParameterizedType) genericClass).getRawType();
			} else if (genericClass instanceof GenericArrayType) { // 处理数组泛型
				return (Class<?>) ((GenericArrayType) genericClass).getGenericComponentType();
			} else {
				return (Class<?>) genericClass;
			}
		} catch (Throwable e) {
			throw new IllegalArgumentException(cls.getName() + " generic type undefined!", e);
		}
	}

	/**
	 * get method name. "void do(int)", "void do()", "int
	 * do(java.lang.String,boolean)"
	 *
	 * @param m method.
	 * @return name.
	 */
	public static String getName(final Method m) {
		StringBuilder ret = new StringBuilder();
		ret.append(getName(m.getReturnType())).append(' ');
		ret.append(m.getName()).append('(');
		Class<?>[] parameterTypes = m.getParameterTypes();
		for (int i = 0; i < parameterTypes.length; i++) {
			if (i > 0) {
				ret.append(',');
			}
			ret.append(getName(parameterTypes[i]));
		}
		ret.append(')');
		return ret.toString();
	}

	public static String getSignature(String methodName, Class<?>[] parameterTypes) {
		StringBuilder sb = new StringBuilder(methodName);
		sb.append("(");
		if (parameterTypes != null && parameterTypes.length > 0) {
			boolean first = true;
			for (Class<?> type : parameterTypes) {
				if (first) {
					first = false;
				} else {
					sb.append(",");
				}
				sb.append(type.getName());
			}
		}
		sb.append(")");
		return sb.toString();
	}

	/**
	 * get constructor name. "()", "(java.lang.String,int)"
	 *
	 * @param c constructor.
	 * @return name.
	 */
	public static String getName(final Constructor<?> c) {
		StringBuilder ret = new StringBuilder("(");
		Class<?>[] parameterTypes = c.getParameterTypes();
		for (int i = 0; i < parameterTypes.length; i++) {
			if (i > 0) {
				ret.append(',');
			}
			ret.append(getName(parameterTypes[i]));
		}
		ret.append(')');
		return ret.toString();
	}

	/**
	 * get class desc. boolean[].class => "[Z" Object.class => "Ljava/lang/Object;"
	 *
	 * @param c class.
	 * @return desc.
	 * @throws NotFoundException
	 */
	public static String getDesc(Class<?> c) {
		StringBuilder ret = new StringBuilder();

		while (c.isArray()) {
			ret.append('[');
			c = c.getComponentType();
		}

		if (c.isPrimitive()) {
			String t = c.getName();
			if ("void".equals(t)) {
				ret.append(JVM_VOID);
			} else if ("boolean".equals(t)) {
				ret.append(JVM_BOOLEAN);
			} else if ("byte".equals(t)) {
				ret.append(JVM_BYTE);
			} else if ("char".equals(t)) {
				ret.append(JVM_CHAR);
			} else if ("double".equals(t)) {
				ret.append(JVM_DOUBLE);
			} else if ("float".equals(t)) {
				ret.append(JVM_FLOAT);
			} else if ("int".equals(t)) {
				ret.append(JVM_INT);
			} else if ("long".equals(t)) {
				ret.append(JVM_LONG);
			} else if ("short".equals(t)) {
				ret.append(JVM_SHORT);
			}
		} else {
			ret.append('L');
			ret.append(c.getName().replace('.', '/'));
			ret.append(';');
		}
		return ret.toString();
	}

	/**
	 * get class array desc. [int.class, boolean[].class, Object.class] =>
	 * "I[ZLjava/lang/Object;"
	 *
	 * @param cs class array.
	 * @return desc.
	 * @throws NotFoundException
	 */
	public static String getDesc(final Class<?>[] cs) {
		if (cs.length == 0) {
			return "";
		}

		StringBuilder sb = new StringBuilder(64);
		for (Class<?> c : cs) {
			sb.append(getDesc(c));
		}
		return sb.toString();
	}

	/**
	 * get method desc
	 *
	 * @param m method.
	 * @return desc.
	 */
	public static String getDesc(final Method m) {
		StringBuilder ret = new StringBuilder(m.getName()).append('(');
		Class<?>[] parameterTypes = m.getParameterTypes();
		for (int i = 0; i < parameterTypes.length; i++) {
			ret.append(getDesc(parameterTypes[i]));
		}
		ret.append(')').append(getDesc(m.getReturnType()));
		return ret.toString();
	}

	/**
	 * get constructor desc
	 *
	 * @param c constructor.
	 * @return desc
	 */
	public static String getDesc(final Constructor<?> c) {
		StringBuilder ret = new StringBuilder("(");
		Class<?>[] parameterTypes = c.getParameterTypes();
		for (int i = 0; i < parameterTypes.length; i++) {
			ret.append(getDesc(parameterTypes[i]));
		}
		ret.append(')').append('V');
		return ret.toString();
	}

	/**
	 * get method desc
	 *
	 * @param m method.
	 * @return desc.
	 */
	public static String getDescWithoutMethodName(Method m) {
		StringBuilder ret = new StringBuilder();
		ret.append('(');
		Class<?>[] parameterTypes = m.getParameterTypes();
		for (int i = 0; i < parameterTypes.length; i++) {
			ret.append(getDesc(parameterTypes[i]));
		}
		ret.append(')').append(getDesc(m.getReturnType()));
		return ret.toString();
	}

	/**
	 * get class desc
	 * 
	 * @param c class.
	 * @return desc.
	 * @throws NotFoundException
	 */
	public static String getDesc(final CtClass c) throws NotFoundException {
		StringBuilder ret = new StringBuilder();
		if (c.isArray()) {
			ret.append('[');
			ret.append(getDesc(c.getComponentType()));
		} else if (c.isPrimitive()) {
			String t = c.getName();
			if ("void".equals(t)) {
				ret.append(JVM_VOID);
			} else if ("boolean".equals(t)) {
				ret.append(JVM_BOOLEAN);
			} else if ("byte".equals(t)) {
				ret.append(JVM_BYTE);
			} else if ("char".equals(t)) {
				ret.append(JVM_CHAR);
			} else if ("double".equals(t)) {
				ret.append(JVM_DOUBLE);
			} else if ("float".equals(t)) {
				ret.append(JVM_FLOAT);
			} else if ("int".equals(t)) {
				ret.append(JVM_INT);
			} else if ("long".equals(t)) {
				ret.append(JVM_LONG);
			} else if ("short".equals(t)) {
				ret.append(JVM_SHORT);
			}
		} else {
			ret.append('L');
			ret.append(c.getName().replace('.', '/'));
			ret.append(';');
		}
		return ret.toString();
	}

	/**
	 * get method desc
	 *
	 * @param m method.
	 * @return desc.
	 */
	public static String getDesc(final CtMethod m) throws NotFoundException {
		StringBuilder ret = new StringBuilder(m.getName()).append('(');
		CtClass[] parameterTypes = m.getParameterTypes();
		for (int i = 0; i < parameterTypes.length; i++) {
			ret.append(getDesc(parameterTypes[i]));
		}
		ret.append(')').append(getDesc(m.getReturnType()));
		return ret.toString();
	}

	/**
	 * get constructor desc
	 *
	 * @param c constructor.
	 * @return desc
	 */
	public static String getDesc(final CtConstructor c) throws NotFoundException {
		StringBuilder ret = new StringBuilder("(");
		CtClass[] parameterTypes = c.getParameterTypes();
		for (int i = 0; i < parameterTypes.length; i++) {
			ret.append(getDesc(parameterTypes[i]));
		}
		ret.append(')').append('V');
		return ret.toString();
	}

	/**
	 * get method desc
	 *
	 * @param m method.
	 * @return desc.
	 */
	public static String getDescWithoutMethodName(final CtMethod m) throws NotFoundException {
		StringBuilder ret = new StringBuilder();
		ret.append('(');
		CtClass[] parameterTypes = m.getParameterTypes();
		for (int i = 0; i < parameterTypes.length; i++) {
			ret.append(getDesc(parameterTypes[i]));
		}
		ret.append(')').append(getDesc(m.getReturnType()));
		return ret.toString();
	}

	/**
	 * name to desc
	 *
	 * @param name name.
	 * @return desc.
	 */
	public static String name2desc(String name) {
		StringBuilder sb = new StringBuilder();
		int c = 0, index = name.indexOf('[');
		if (index > 0) {
			c = (name.length() - index) / 2;
			name = name.substring(0, index);
		}
		while (c-- > 0) {
			sb.append("[");
		}
		if ("void".equals(name)) {
			sb.append(JVM_VOID);
		} else if ("boolean".equals(name)) {
			sb.append(JVM_BOOLEAN);
		} else if ("byte".equals(name)) {
			sb.append(JVM_BYTE);
		} else if ("char".equals(name)) {
			sb.append(JVM_CHAR);
		} else if ("double".equals(name)) {
			sb.append(JVM_DOUBLE);
		} else if ("float".equals(name)) {
			sb.append(JVM_FLOAT);
		} else if ("int".equals(name)) {
			sb.append(JVM_INT);
		} else if ("long".equals(name)) {
			sb.append(JVM_LONG);
		} else if ("short".equals(name)) {
			sb.append(JVM_SHORT);
		} else {
			sb.append('L').append(name.replace('.', '/')).append(';');
		}
		return sb.toString();
	}

	/**
	 * desc to name
	 *
	 * @param desc desc.
	 * @return name.
	 */
	public static String desc2name(String desc) {
		StringBuilder sb = new StringBuilder();
		int c = desc.lastIndexOf('[') + 1;
		if (desc.length() == c + 1) {
			switch (desc.charAt(c)) {
			case JVM_VOID: {
				sb.append("void");
				break;
			}
			case JVM_BOOLEAN: {
				sb.append("boolean");
				break;
			}
			case JVM_BYTE: {
				sb.append("byte");
				break;
			}
			case JVM_CHAR: {
				sb.append("char");
				break;
			}
			case JVM_DOUBLE: {
				sb.append("double");
				break;
			}
			case JVM_FLOAT: {
				sb.append("float");
				break;
			}
			case JVM_INT: {
				sb.append("int");
				break;
			}
			case JVM_LONG: {
				sb.append("long");
				break;
			}
			case JVM_SHORT: {
				sb.append("short");
				break;
			}
			default:
				throw new RuntimeException();
			}
		} else {
			sb.append(desc.substring(c + 1, desc.length() - 1).replace('/', '.'));
		}
		while (c-- > 0) {
			sb.append("[]");
		}
		return sb.toString();
	}

	public static Class<?> forName(String name) {
		try {
			return name2class(name);
		} catch (ClassNotFoundException e) {
			throw new IllegalStateException("Not found class " + name + ", cause: " + e.getMessage(), e);
		}
	}

	/**
	 * name to class.
	 *
	 * @param name name.
	 * @return Class instance.
	 */
	public static Class<?> name2class(String name) throws ClassNotFoundException {
		return name2class(ClassHelper.getClassLoader(), name);
	}

	/**
	 * name to class.
	 *
	 * @param cl   ClassLoader instance.
	 * @param name name.
	 * @return Class instance.
	 */
	private static Class<?> name2class(ClassLoader cl, String name) throws ClassNotFoundException {
		int c = 0, index = name.indexOf('[');
		if (index > 0) {
			c = (name.length() - index) / 2;
			name = name.substring(0, index);
		}
		if (c > 0) {
			StringBuilder sb = new StringBuilder();
			while (c-- > 0) {
				sb.append("[");
			}

			if ("void".equals(name)) {
				sb.append(JVM_VOID);
			} else if ("boolean".equals(name)) {
				sb.append(JVM_BOOLEAN);
			} else if ("byte".equals(name)) {
				sb.append(JVM_BYTE);
			} else if ("char".equals(name)) {
				sb.append(JVM_CHAR);
			} else if ("double".equals(name)) {
				sb.append(JVM_DOUBLE);
			} else if ("float".equals(name)) {
				sb.append(JVM_FLOAT);
			} else if ("int".equals(name)) {
				sb.append(JVM_INT);
			} else if ("long".equals(name)) {
				sb.append(JVM_LONG);
			} else if ("short".equals(name)) {
				sb.append(JVM_SHORT);
			} else {
				sb.append('L').append(name).append(';'); // "java.lang.Object" ==> "Ljava.lang.Object;"
			}
			name = sb.toString();
		} else {
			if ("void".equals(name)) {
				return void.class;
			} else if ("boolean".equals(name)) {
				return boolean.class;
			} else if ("byte".equals(name)) {
				return byte.class;
			} else if ("char".equals(name)) {
				return char.class;
			} else if ("double".equals(name)) {
				return double.class;
			} else if ("float".equals(name)) {
				return float.class;
			} else if ("int".equals(name)) {
				return int.class;
			} else if ("long".equals(name)) {
				return long.class;
			} else if ("short".equals(name)) {
				return short.class;
			}
		}

		if (cl == null) {
			cl = ClassHelper.getClassLoader();
		}
		Class<?> clazz = NAME_CLASS_CACHE.get(name);
		if (clazz == null) {
			clazz = Class.forName(name, true, cl);
			NAME_CLASS_CACHE.put(name, clazz);
		}
		return clazz;
	}

	/**
	 * desc to class.
	 *
	 * @param desc desc.
	 * @return Class instance.
	 * @throws ClassNotFoundException
	 */
	public static Class<?> desc2class(String desc) throws ClassNotFoundException {
		return desc2class(ClassHelper.getClassLoader(), desc);
	}

	/**
	 * desc to class. "[Z" => boolean[].class "[[Ljava/util/Map;" =>
	 * java.util.Map[][].class
	 *
	 * @param cl   ClassLoader instance.
	 * @param desc desc.
	 * @return Class instance.
	 * @throws ClassNotFoundException
	 */
	private static Class<?> desc2class(ClassLoader cl, String desc) throws ClassNotFoundException {
		switch (desc.charAt(0)) {
		case JVM_VOID:
			return void.class;
		case JVM_BOOLEAN:
			return boolean.class;
		case JVM_BYTE:
			return byte.class;
		case JVM_CHAR:
			return char.class;
		case JVM_DOUBLE:
			return double.class;
		case JVM_FLOAT:
			return float.class;
		case JVM_INT:
			return int.class;
		case JVM_LONG:
			return long.class;
		case JVM_SHORT:
			return short.class;
		case 'L':
			desc = desc.substring(1, desc.length() - 1).replace('/', '.'); // "Ljava/lang/Object;" ==>
			// "java.lang.Object"
			break;
		case '[':
			desc = desc.replace('/', '.'); // "[[Ljava/lang/Object;" ==> "[[Ljava.lang.Object;"
			break;
		default:
			throw new ClassNotFoundException("Class not found: " + desc);
		}

		if (cl == null) {
			cl = ClassHelper.getClassLoader();
		}
		Class<?> clazz = DESC_CLASS_CACHE.get(desc);
		if (clazz == null) {
			clazz = Class.forName(desc, true, cl);
			DESC_CLASS_CACHE.put(desc, clazz);
		}
		return clazz;
	}

	/**
	 * get class array instance.
	 *
	 * @param desc desc.
	 * @return Class class array.
	 * @throws ClassNotFoundException
	 */
	public static Class<?>[] desc2classArray(String desc) throws ClassNotFoundException {
		return desc2classArray(ClassHelper.getClassLoader(), desc);
	}

	/**
	 * get class array instance.
	 *
	 * @param cl   ClassLoader instance.
	 * @param desc desc.
	 * @return Class[] class array.
	 * @throws ClassNotFoundException
	 */
	private static Class<?>[] desc2classArray(ClassLoader cl, String desc) throws ClassNotFoundException {
		if (desc.length() == 0) {
			return EMPTY_CLASS_ARRAY;
		}

		List<Class<?>> cs = new ArrayList<Class<?>>();
		Matcher m = DESC_PATTERN.matcher(desc);
		while (m.find()) {
			cs.add(desc2class(cl, m.group()));
		}
		return cs.toArray(EMPTY_CLASS_ARRAY);
	}

	/**
	 * 根据方法签名从类中找出方法。
	 *
	 * @param clazz      查找的类。
	 * @param methodName 方法签名，形如method1(int, String)。也允许只给方法名不参数只有方法名，形如method2。
	 * @return 返回查找到的方法。
	 * @throws NoSuchMethodException
	 * @throws ClassNotFoundException
	 * @throws IllegalStateException  给定的方法签名找到多个方法（方法签名中没有指定参数，又有有重载的方法的情况）
	 */
	public static Method findMethodByMethodSignature(Class<?> clazz, String methodName, String[] parameterTypes)
			throws NoSuchMethodException, ClassNotFoundException {
		String signature = methodName;
		if (parameterTypes != null && parameterTypes.length > 0) {
			signature = methodName + StringUtils.join(parameterTypes);
		}
		Method method = SIGNATURE_METHODS_CACHE.get(signature);
		if (method != null) {
			return method;
		}
		if (parameterTypes == null) {
			List<Method> finded = new ArrayList<Method>();
			for (Method m : clazz.getMethods()) {
				if (m.getName().equals(methodName)) {
					finded.add(m);
				}
			}
			if (finded.isEmpty()) {
				throw new NoSuchMethodException("No such method " + methodName + " in class " + clazz);
			}
			if (finded.size() > 1) {
				String msg = String.format("Not unique method for method name(%s) in class(%s), find %d methods.",
						methodName, clazz.getName(), finded.size());
				throw new IllegalStateException(msg);
			}
			method = finded.get(0);
		} else {
			Class<?>[] types = new Class<?>[parameterTypes.length];
			for (int i = 0; i < parameterTypes.length; i++) {
				types[i] = ReflectUtils.name2class(parameterTypes[i]);
			}
			method = clazz.getMethod(methodName, types);

		}
		SIGNATURE_METHODS_CACHE.put(signature, method);
		return method;
	}

	public static Method findMethodByMethodName(Class<?> clazz, String methodName)
			throws NoSuchMethodException, ClassNotFoundException {
		return findMethodByMethodSignature(clazz, methodName, null);
	}

	public static Constructor<?> findConstructor(Class<?> clazz, Class<?> paramType) throws NoSuchMethodException {
		Constructor<?> targetConstructor;
		try {
			targetConstructor = clazz.getConstructor(new Class<?>[] { paramType });
		} catch (NoSuchMethodException e) {
			targetConstructor = null;
			Constructor<?>[] constructors = clazz.getConstructors();
			for (Constructor<?> constructor : constructors) {
				if (Modifier.isPublic(constructor.getModifiers()) && constructor.getParameterTypes().length == 1
						&& constructor.getParameterTypes()[0].isAssignableFrom(paramType)) {
					targetConstructor = constructor;
					break;
				}
			}
			if (targetConstructor == null) {
				throw e;
			}
		}
		return targetConstructor;
	}

	/**
	 * 检查对象是否是指定接口的实现。
	 * <p>
	 * 不会触发到指定接口的{@link Class}，所以如果ClassLoader中没有指定接口类时，也不会出错。
	 *
	 * @param obj                要检查的对象
	 * @param interfaceClazzName 指定的接口名
	 * @return 返回{@code true}，如果对象实现了指定接口；否则返回{@code false}。
	 */
	public static boolean isInstance(Object obj, String interfaceClazzName) {
		for (Class<?> clazz = obj.getClass(); clazz != null
				&& !clazz.equals(Object.class); clazz = clazz.getSuperclass()) {
			Class<?>[] interfaces = clazz.getInterfaces();
			for (Class<?> itf : interfaces) {
				if (itf.getName().equals(interfaceClazzName)) {
					return true;
				}
			}
		}
		return false;
	}

	public static Object getEmptyObject(Class<?> returnType) {
		return getEmptyObject(returnType, new HashMap<Class<?>, Object>(), 0);
	}

	private static Object getEmptyObject(Class<?> returnType, Map<Class<?>, Object> emptyInstances, int level) {
		if (level > 2) {
			return null;
		}
		if (returnType == null) {
			return null;
		} else if (returnType == boolean.class || returnType == Boolean.class) {
			return false;
		} else if (returnType == char.class || returnType == Character.class) {
			return '\0';
		} else if (returnType == byte.class || returnType == Byte.class) {
			return (byte) 0;
		} else if (returnType == short.class || returnType == Short.class) {
			return (short) 0;
		} else if (returnType == int.class || returnType == Integer.class) {
			return 0;
		} else if (returnType == long.class || returnType == Long.class) {
			return 0L;
		} else if (returnType == float.class || returnType == Float.class) {
			return 0F;
		} else if (returnType == double.class || returnType == Double.class) {
			return 0D;
		} else if (returnType.isArray()) {
			return Array.newInstance(returnType.getComponentType(), 0);
		} else if (returnType.isAssignableFrom(ArrayList.class)) {
			return new ArrayList<Object>(0);
		} else if (returnType.isAssignableFrom(HashSet.class)) {
			return new HashSet<Object>(0);
		} else if (returnType.isAssignableFrom(HashMap.class)) {
			return new HashMap<Object, Object>(0);
		} else if (String.class.equals(returnType)) {
			return "";
		} else if (!returnType.isInterface()) {
			try {
				Object value = emptyInstances.get(returnType);
				if (value == null) {
					value = returnType.newInstance();
					emptyInstances.put(returnType, value);
				}
				Class<?> cls = value.getClass();
				while (cls != null && cls != Object.class) {
					Field[] fields = cls.getDeclaredFields();
					for (Field field : fields) {
						Object property = getEmptyObject(field.getType(), emptyInstances, level + 1);
						if (property != null) {
							try {
								if (!field.isAccessible()) {
									field.setAccessible(true);
								}
								field.set(value, property);
							} catch (Throwable e) {
							}
						}
					}
					cls = cls.getSuperclass();
				}
				return value;
			} catch (Throwable e) {
				return null;
			}
		} else {
			return null;
		}
	}

	public static boolean isBeanPropertyReadMethod(Method method) {
		return method != null && Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers())
				&& method.getReturnType() != void.class && method.getDeclaringClass() != Object.class
				&& method.getParameterTypes().length == 0
				&& ((method.getName().startsWith("get") && method.getName().length() > 3)
						|| (method.getName().startsWith("is") && method.getName().length() > 2));
	}

	public static String getPropertyNameFromBeanReadMethod(Method method) {
		if (isBeanPropertyReadMethod(method)) {
			if (method.getName().startsWith("get")) {
				return method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4);
			}
			if (method.getName().startsWith("is")) {
				return method.getName().substring(2, 3).toLowerCase() + method.getName().substring(3);
			}
		}
		return null;
	}

	public static boolean isBeanPropertyWriteMethod(Method method) {
		return method != null && Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers())
				&& method.getDeclaringClass() != Object.class && method.getParameterTypes().length == 1
				&& method.getName().startsWith("set") && method.getName().length() > 3;
	}

	public static String getPropertyNameFromBeanWriteMethod(Method method) {
		if (isBeanPropertyWriteMethod(method)) {
			return method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4);
		}
		return null;
	}

	public static boolean isPublicInstanceField(Field field) {
		return Modifier.isPublic(field.getModifiers()) && !Modifier.isStatic(field.getModifiers())
				&& !Modifier.isFinal(field.getModifiers()) && !field.isSynthetic();
	}

	/**
	 * 获得所有的字段，包括父类
	 *
	 * @param cl
	 * @return
	 */
	public static Map<String, Field> getBeanPropertyFields(Class<?> cl) {
		Map<String, Field> properties = new HashMap<String, Field>();
		for (; cl != null; cl = cl.getSuperclass()) {
			Field[] fields = cl.getDeclaredFields();
			for (Field field : fields) {
				if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) {
					continue;
				}

				field.setAccessible(true);

				properties.put(field.getName(), field);
			}
		}

		return properties;
	}

	public static Map<String, Method> getBeanPropertyReadMethods(Class<?> cl) {
		Map<String, Method> properties = new HashMap<String, Method>();
		for (; cl != null; cl = cl.getSuperclass()) {
			Method[] methods = cl.getDeclaredMethods();
			for (Method method : methods) {
				if (isBeanPropertyReadMethod(method)) {
					method.setAccessible(true);
					String property = getPropertyNameFromBeanReadMethod(method);
					properties.put(property, method);
				}
			}
		}

		return properties;
	}

	public static String forceCast(Class<?> clazz) {
		if (int.class.equals(clazz)) {
			return Integer.class.getName();
		}

		if (long.class.equals(clazz)) {
			return Long.class.getName();
		}

		if (boolean.class.equals(clazz)) {
			return Boolean.class.getName();
		}

		if (double.class.equals(clazz)) {
			return Double.class.getName();
		}

		if (float.class.equals(clazz)) {
			return Float.class.getName();
		}

		if (short.class.equals(clazz)) {
			return Short.class.getName();
		}

		if (byte.class.equals(clazz)) {
			return Byte.class.getName();
		}

		if (char.class.equals(clazz)) {
			return Character.class.getName();
		}

		return clazz.getName();
	}

	public static String box(Class<?> clazz, String value) {
		if (int.class.equals(clazz)) {
			return Integer.class.getName() + ".valueOf(" + value + ")";
		}

		if (long.class.equals(clazz)) {
			return Long.class.getName() + ".valueOf(" + value + ")";
		}

		if (boolean.class.equals(clazz)) {
			return Boolean.class.getName() + ".valueOf(" + value + ")";
		}

		if (double.class.equals(clazz)) {
			return Double.class.getName() + ".valueOf(" + value + ")";
		}

		if (float.class.equals(clazz)) {
			return Float.class.getName() + ".valueOf(" + value + ")";
		}

		if (short.class.equals(clazz)) {
			return Short.class.getName() + ".valueOf(" + value + ")";
		}

		if (byte.class.equals(clazz)) {
			return Byte.class.getName() + ".valueOf(" + value + ")";
		}

		if (char.class.equals(clazz)) {
			return Character.class.getName() + ".valueOf(" + value + ")";
		}

		return value;
	}

	public static String unbox(Class<?> clazz) {
		if (int.class.equals(clazz)) {
			return ".intValue()";
		}

		if (long.class.equals(clazz)) {
			return ".longValue()";
		}

		if (boolean.class.equals(clazz)) {
			return ".booleanValue()";
		}

		if (double.class.equals(clazz)) {
			return ".doubleValue()";
		}

		if (float.class.equals(clazz)) {
			return ".floatValue()";
		}

		if (short.class.equals(clazz)) {
			return ".shortValue()";
		}

		if (byte.class.equals(clazz)) {
			return ".byteValue()";
		}

		if (char.class.equals(clazz)) {
			return ".charValue()";
		}

		return "";
	}

	/**
	 * 获取方法的标示 : method_name + "(" + paramDesc + ")"
	 *
	 * @param method
	 * @return
	 */
	public static String getMethodDesc(Method method) {
		String methodParamDesc = getMethodParamDesc(method);
		return getMethodDesc(method.getName(), methodParamDesc);
	}

	/**
	 * 获取method方式的接口参数，以逗号分割，拼接clz列表。 如果没有参数，那么void表示
	 *
	 * @param method
	 * @return
	 */
	public static String getMethodParamDesc(Method method) {
		if (method.getParameterTypes() == null || method.getParameterTypes().length == 0) {
			return EMPTY_PARAM;
		}

		StringBuilder builder = new StringBuilder();

		Class<?>[] clzs = method.getParameterTypes();

		for (Class<?> clz : clzs) {
			String className = getName(clz);
			builder.append(className).append(PARAM_CLASS_SPLIT);
		}

		return builder.substring(0, builder.length() - 1);
	}

	/**
	 * 获取方法的标示 : method_name + "(" + paramDesc + ")"
	 *
	 * @param
	 * @return
	 */
	public static String getMethodDesc(String methodName, String paramDesc) {
		if (paramDesc == null) {
			return methodName + "()";
		} else {
			return methodName + "(" + paramDesc + ")";
		}
	}

	/**
	 * 标准的属性名称
	 *
	 * @param name
	 * @return
	 */
	public static String propertyName(String name) {
		if (name == null || name.length() == 0) {
			return name;
		}
		if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))) {
			return name;
		}
		char chars[] = name.toCharArray();
		chars[0] = Character.toLowerCase(chars[0]);
		return new String(chars);
	}

	/**
	 * 获得 指定类型的指定名称的字段
	 *
	 * @param clazz
	 * @param fieldName
	 * @param declaredFields
	 * @return
	 */
	public static Field getField(Class<?> clazz, String fieldName, Field[] declaredFields) {
		for (Field field : declaredFields) {
			String itemName = field.getName();
			if (fieldName.equals(itemName)) {
				return field;
			}

			char c0, c1;
			if (fieldName.length() > 2 && (c0 = fieldName.charAt(0)) >= 'a' && c0 <= 'z'
					&& (c1 = fieldName.charAt(1)) >= 'A' && c1 <= 'Z' && fieldName.equalsIgnoreCase(itemName)) {
				return field;
			}
		}
		Class<?> superClass = clazz.getSuperclass();
		if (superClass != null && superClass != Object.class) {
			return getField(superClass, fieldName, superClass.getDeclaredFields());
		}
		return null;
	}

	/**
	 * 得到实际的类型
	 *
	 * @param type
	 * @return
	 */
	public static Class<?> getClass(Type type) {
		if (type.getClass() == Class.class) {
			return (Class<?>) type;
		}

		if (type instanceof ParameterizedType) {
			return getClass(((ParameterizedType) type).getRawType());
		}

		if (type instanceof TypeVariable) {
			Type boundType = ((TypeVariable<?>) type).getBounds()[0];
			return (Class<?>) boundType;
		}

		if (type instanceof WildcardType) {
			Type[] upperBounds = ((WildcardType) type).getUpperBounds();
			if (upperBounds.length == 1) {
				return getClass(upperBounds[0]);
			}
		}

		return Object.class;
	}

	/**
	 * 是否范型类型
	 *
	 * @param type
	 * @return
	 */
	public static boolean isGenericParamType(Type type) {
		if (type instanceof ParameterizedType) {
			return true;
		}
		if (type instanceof Class) {
			Type superType = ((Class<?>) type).getGenericSuperclass();
			return superType != Object.class && isGenericParamType(superType);
		}
		return false;
	}

	/**
	 * 得到范型类型
	 *
	 * @param type
	 * @return
	 */
	public static Type getGenericParamType(Type type) {
		if (type instanceof ParameterizedType) {
			return type;
		}
		if (type instanceof Class) {
			return getGenericParamType(((Class<?>) type).getGenericSuperclass());
		}
		return type;
	}

	/**
	 * 获得字段的的类型
	 *
	 * @param clazz     类
	 * @param fieldType 字段类型
	 * @return 类型
	 */
	public static Type getFieldType(final Class<?> clazz, Type fieldType) {
		if (clazz == null) {
			return fieldType;
		}

		if (fieldType instanceof GenericArrayType) {
			GenericArrayType genericArrayType = (GenericArrayType) fieldType;
			Type componentType = genericArrayType.getGenericComponentType();
			Type componentTypeX = getFieldType(clazz, componentType);
			if (componentType != componentTypeX) {
				Type fieldTypeX = Array.newInstance(ReflectUtils.getClass(componentTypeX), 0).getClass();
				return fieldTypeX;
			}

			return fieldType;
		}

		Type type = (Type) clazz;
		if (!ReflectUtils.isGenericParamType(type)) {
			return fieldType;
		}

		if (fieldType instanceof TypeVariable) {
			ParameterizedType paramType = (ParameterizedType) ReflectUtils.getGenericParamType(type);
			Class<?> parameterizedClass = ReflectUtils.getClass(paramType);
			final TypeVariable<?> typeVar = (TypeVariable<?>) fieldType;

			TypeVariable<?>[] typeVariables = parameterizedClass.getTypeParameters();
			for (int i = 0; i < typeVariables.length; ++i) {
				if (typeVariables[i].getName().equals(typeVar.getName())) {
					fieldType = paramType.getActualTypeArguments()[i];
					return fieldType;
				}
			}
		}

		if (fieldType instanceof ParameterizedType) {
			ParameterizedType parameterizedFieldType = (ParameterizedType) fieldType;

			Type[] arguments = parameterizedFieldType.getActualTypeArguments();
			TypeVariable<?>[] typeVariables;
			ParameterizedType paramType;
			if (type instanceof ParameterizedType) {
				paramType = (ParameterizedType) type;
				typeVariables = clazz.getTypeParameters();
			} else if (clazz.getGenericSuperclass() instanceof ParameterizedType) {
				paramType = (ParameterizedType) clazz.getGenericSuperclass();
				typeVariables = clazz.getSuperclass().getTypeParameters();
			} else {
				paramType = parameterizedFieldType;
				typeVariables = type.getClass().getTypeParameters();
			}

			boolean changed = getArgument(arguments, typeVariables, paramType.getActualTypeArguments());
			if (changed) {
				fieldType = new ParameterizedTypeImpl(arguments, parameterizedFieldType.getOwnerType(),
						parameterizedFieldType.getRawType());
				return fieldType;
			}
		}

		return fieldType;
	}

	/**
	 * tv : getGenericType, getGenericReturnType, getGenericParameterTypes clazz :
	 * 所属的类型
	 * <p>
	 * 感觉 getFieldType 就可以获得实际的类型
	 * <p>
	 * 获得类型变量的实际类型
	 * <p>
	 * BaseEntity<Long> 中 id 的实际类型
	 */
	public static Type getInheritGenericType(Class<?> clazz, TypeVariable<?> tv) {
		GenericDeclaration gd = tv.getGenericDeclaration();

		Class<?> class_gd = null;
		if (gd instanceof Class) {
			class_gd = (Class<?>) tv.getGenericDeclaration();
		}

		Type[] arguments = null;
		if (class_gd == clazz) {
			if ((Type) clazz instanceof ParameterizedType) {
				ParameterizedType ptype = (ParameterizedType) (Type) clazz;
				arguments = ptype.getActualTypeArguments();
			}
		} else {
			for (Class<?> c = clazz; c != null && c != Object.class && c != class_gd; c = c.getSuperclass()) {
				Type superType = c.getGenericSuperclass();

				if (superType instanceof ParameterizedType) {
					ParameterizedType p_superType = (ParameterizedType) superType;
					Type[] p_superType_args = p_superType.getActualTypeArguments();
					getArgument(p_superType_args, c.getTypeParameters(), arguments);
					arguments = p_superType_args;
				}
			}
		}

		if (arguments == null || class_gd == null) {
			return null;
		}

		Type actualType = null;
		TypeVariable<?>[] typeVariables = class_gd.getTypeParameters();
		for (int j = 0; j < typeVariables.length; ++j) {
			if (tv.equals(typeVariables[j])) {
				actualType = arguments[j];
				break;
			}
		}

		return actualType;
	}

	public static boolean getArgument(Type[] typeArgs, TypeVariable<?>[] typeVariables, Type[] arguments) {
		if (arguments == null || typeVariables.length == 0) {
			return false;
		}

		boolean changed = false;
		for (int i = 0; i < typeArgs.length; ++i) {
			Type typeArg = typeArgs[i];
			if (typeArg instanceof ParameterizedType) {
				ParameterizedType p_typeArg = (ParameterizedType) typeArg;
				Type[] p_typeArg_args = p_typeArg.getActualTypeArguments();
				boolean p_changed = getArgument(p_typeArg_args, typeVariables, arguments);
				if (p_changed) {
					typeArgs[i] = new ParameterizedTypeImpl(p_typeArg_args, p_typeArg.getOwnerType(),
							p_typeArg.getRawType());
					changed = true;
				}
			} else if (typeArg instanceof TypeVariable) {
				for (int j = 0; j < typeVariables.length; ++j) {
					if (typeArg.equals(typeVariables[j])) {
						typeArgs[i] = arguments[j];
						changed = true;
					}
				}
			}
		}

		return changed;
	}

	private ReflectUtils() {
	}
}